Implement the side windows.
authorHavoc Pennington <hp@pobox.com>
Fri, 29 Sep 2000 05:47:34 +0000 (05:47 +0000)
committerHavoc Pennington <hp@src.gnome.org>
Fri, 29 Sep 2000 05:47:34 +0000 (05:47 +0000)
2000-09-29  Havoc Pennington  <hp@pobox.com>

* gtk/gtktextview.c: Implement the side windows.

* gtk/testtext.c: Implement simple line numbering in the
left side window; seems to make scrolling sloooow. Oops.
Also, cursor blink is for some reason causing redraws
of the line numbers. Should investigate...

ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtktextview.c
gtk/testtext.c
tests/testtext.c

index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index 98f56c3b4c21c994c2c7587e5611a273e1c90f4e..f197d8277c3eef6874d125842418bb8050e2c694 100644 (file)
@@ -1,3 +1,12 @@
+2000-09-29  Havoc Pennington  <hp@pobox.com>
+
+       * gtk/gtktextview.c: Implement the side windows.
+
+       * gtk/testtext.c: Implement simple line numbering in the 
+       left side window; seems to make scrolling sloooow. Oops.
+       Also, cursor blink is for some reason causing redraws
+       of the line numbers. Should investigate...
+
 2000-09-28  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c: Set up infrastructure to deal with lots of 
index ba19f816d0ca88e8181107e9bb6c5313a5c01cd9..aedba9469e8a0b81b65dbf6b5afcd349e0d2e687 100644 (file)
@@ -1104,6 +1104,18 @@ gtk_text_view_finalize (GObject *object)
     gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
 
   text_window_free (text_view->text_window);
+
+  if (text_view->left_window)
+    text_window_free (text_view->left_window);
+
+  if (text_view->top_window)
+    text_window_free (text_view->top_window);
+
+  if (text_view->right_window)
+    text_window_free (text_view->right_window);
+  
+  if (text_view->bottom_window)
+    text_window_free (text_view->bottom_window);
   
   gtk_object_unref (GTK_OBJECT (text_view->im_context));  
   
@@ -1192,6 +1204,18 @@ gtk_text_view_size_request (GtkWidget      *widget,
 
   requisition->width = text_view->text_window->requisition.width + FOCUS_EDGE_WIDTH * 2;
   requisition->height = text_view->text_window->requisition.height + FOCUS_EDGE_WIDTH * 2;
+
+  if (text_view->left_window)
+    requisition->width += text_view->left_window->requisition.width;
+
+  if (text_view->right_window)
+    requisition->width += text_view->right_window->requisition.width;
+
+  if (text_view->top_window)
+    requisition->height += text_view->top_window->requisition.height;
+
+  if (text_view->bottom_window)
+    requisition->height += text_view->bottom_window->requisition.height;
 }
 
 static void
@@ -1203,7 +1227,12 @@ gtk_text_view_size_allocate (GtkWidget *widget,
   gint y;
   GtkAdjustment *vadj;
   gboolean yoffset_changed = FALSE;
-  GdkRectangle child_rect;
+  gint width, height;
+  GdkRectangle text_rect;
+  GdkRectangle left_rect;
+  GdkRectangle right_rect;
+  GdkRectangle top_rect;
+  GdkRectangle bottom_rect;
   
   text_view = GTK_TEXT_VIEW (widget);
   
@@ -1215,23 +1244,89 @@ gtk_text_view_size_allocate (GtkWidget *widget,
                               allocation->x, allocation->y,
                               allocation->width, allocation->height);
     }
-  
-  child_rect = *allocation;
 
-  child_rect.x = FOCUS_EDGE_WIDTH;
-  child_rect.y = FOCUS_EDGE_WIDTH;
-  child_rect.width -= FOCUS_EDGE_WIDTH * 2;
-  child_rect.height -= FOCUS_EDGE_WIDTH * 2;
+  /* distribute width/height among child windows. Ensure all
+   * windows get at least a 1x1 allocation.
+   */
 
-  if (child_rect.width < 0 || child_rect.height < 0)
-    {
-      /* Write over the other windows with the text window. */
-      child_rect = *allocation;
-    }
+  width = allocation->width - FOCUS_EDGE_WIDTH * 2;
+
+  if (text_view->left_window)
+    left_rect.width = text_view->left_window->requisition.width;
+  else
+    left_rect.width = 1;
+
+  width -= left_rect.width;
+
+  if (text_view->right_window)
+    right_rect.width = text_view->right_window->requisition.width;
+  else
+    right_rect.width = 1;
+
+  width -= right_rect.width;
+
+  text_rect.width = MAX (1, width);
+
+  top_rect.width = text_rect.width;
+  bottom_rect.width = text_rect.width;
+
+
+  height = allocation->height - FOCUS_EDGE_WIDTH * 2;
+
+  if (text_view->top_window)
+    top_rect.height = text_view->top_window->requisition.height;
+  else
+    top_rect.height = 1;
+
+  height -= top_rect.height;
+
+  if (text_view->bottom_window)
+    bottom_rect.height = text_view->bottom_window->requisition.height;
+  else
+    bottom_rect.height = 1;
+
+  height -= bottom_rect.height;
+
+  text_rect.height = MAX (1, height);
+
+  left_rect.height = text_rect.height;
+  right_rect.height = text_rect.height;
+
+  /* Origins */
+  left_rect.x = FOCUS_EDGE_WIDTH;
+  top_rect.y = FOCUS_EDGE_WIDTH;
+
+  text_rect.x = left_rect.x + left_rect.width;
+  text_rect.y = top_rect.y + top_rect.height;
+  
+  left_rect.y = text_rect.y;
+  right_rect.y = text_rect.y;
+
+  top_rect.x = text_rect.x;
+  bottom_rect.x = text_rect.x;
+  
+  right_rect.x = text_rect.x + text_rect.width;
+  bottom_rect.y = text_rect.y + text_rect.height;  
   
   text_window_size_allocate (text_view->text_window,
-                             &child_rect);
+                             &text_rect);
+
+  if (text_view->left_window)
+    text_window_size_allocate (text_view->left_window,
+                               &left_rect);
 
+  if (text_view->right_window)
+    text_window_size_allocate (text_view->right_window,
+                               &right_rect);
+
+  if (text_view->top_window)
+    text_window_size_allocate (text_view->top_window,
+                               &top_rect);
+
+  if (text_view->bottom_window)
+    text_window_size_allocate (text_view->bottom_window,
+                               &bottom_rect);
+  
   gtk_text_view_ensure_layout (text_view);
   gtk_text_layout_set_screen_width (text_view->layout,
                                     SCREEN_WIDTH (text_view));
@@ -1424,17 +1519,31 @@ gtk_text_view_realize (GtkWidget *widget)
   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
                                   &attributes, attributes_mask);
   gdk_window_set_user_data (widget->window, widget);
-  
-  text_window_realize (text_view->text_window, widget->window);
-  
+
+  /* must come before text_window_realize calls */
   widget->style = gtk_style_attach (widget->style, widget->window);
 
   gdk_window_set_background (widget->window,
-                             &widget->style->base[GTK_WIDGET_STATE (widget)]);
+                             &widget->style->bg[GTK_WIDGET_STATE (widget)]);
   
-  gdk_window_set_background (text_view->text_window->bin_window,
-                             &widget->style->base[GTK_WIDGET_STATE (widget)]);
+  text_window_realize (text_view->text_window, widget->window);
 
+  if (text_view->left_window)
+    text_window_realize (text_view->left_window,
+                         widget->window);
+
+  if (text_view->top_window)
+    text_window_realize (text_view->top_window,
+                         widget->window);
+
+  if (text_view->right_window)
+    text_window_realize (text_view->right_window,
+                         widget->window);
+  
+  if (text_view->bottom_window)
+    text_window_realize (text_view->bottom_window,
+                         widget->window);
+  
   gtk_text_view_ensure_layout (text_view);
 }
 
@@ -1458,6 +1567,18 @@ gtk_text_view_unrealize (GtkWidget *widget)
     }
 
   text_window_unrealize (text_view->text_window);
+
+  if (text_view->left_window)
+    text_window_unrealize (text_view->left_window);
+
+  if (text_view->top_window)
+    text_window_unrealize (text_view->top_window);
+
+  if (text_view->right_window)
+    text_window_unrealize (text_view->right_window);
+  
+  if (text_view->bottom_window)
+    text_window_unrealize (text_view->bottom_window);
   
   gtk_text_view_destroy_layout (text_view);
   
@@ -1473,12 +1594,29 @@ gtk_text_view_style_set (GtkWidget *widget,
   if (GTK_WIDGET_REALIZED (widget))
     {
       gdk_window_set_background (widget->window,
-                                 &widget->style->base[GTK_WIDGET_STATE (widget)]);
+                                 &widget->style->bg[GTK_WIDGET_STATE (widget)]);
       
       gdk_window_set_background (text_view->text_window->bin_window,
                                 &widget->style->base[GTK_WIDGET_STATE (widget)]);
 
-      gtk_text_view_set_attributes_from_style (text_view, text_view->layout->default_style, widget->style);
+      if (text_view->left_window)
+        gdk_window_set_background (text_view->left_window->bin_window,
+                                   &widget->style->bg[GTK_WIDGET_STATE (widget)]);
+      if (text_view->right_window)
+        gdk_window_set_background (text_view->right_window->bin_window,
+                                   &widget->style->bg[GTK_WIDGET_STATE (widget)]);
+
+      if (text_view->top_window)
+        gdk_window_set_background (text_view->top_window->bin_window,
+                                   &widget->style->bg[GTK_WIDGET_STATE (widget)]);      
+
+      if (text_view->bottom_window)
+        gdk_window_set_background (text_view->bottom_window->bin_window,
+                                   &widget->style->bg[GTK_WIDGET_STATE (widget)]);
+      
+      gtk_text_view_set_attributes_from_style (text_view,
+                                               text_view->layout->default_style,
+                                               widget->style);
       gtk_text_layout_default_style_changed (text_view->layout);
     }
 }
@@ -1844,19 +1982,82 @@ gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
                        area->width, area->height);
 }
 
+static void
+send_expose (GtkTextView   *text_view,
+             GtkTextWindow *win,
+             GdkRectangle  *area)
+{
+  GdkEventExpose event;
+  
+  event.type = GDK_EXPOSE;
+  event.send_event = TRUE;
+  event.window = win->bin_window;
+  event.area = *area;
+  event.count = 0;
+
+  /* Fix coordinates (convert widget coords to window coords) */
+  gtk_text_view_window_to_buffer_coords (text_view,
+                                         GTK_TEXT_WINDOW_WIDGET,
+                                         event.area.x,
+                                         event.area.y,
+                                         &event.area.x,
+                                         &event.area.y);
+  
+  gtk_text_view_buffer_to_window_coords (text_view,
+                                         win->type,
+                                         event.area.x,
+                                         event.area.y,
+                                         &event.area.x,
+                                         &event.area.y);
+
+      
+  gdk_window_ref (event.window);
+  gtk_widget_event (GTK_WIDGET (text_view), (GdkEvent*) &event);
+  gdk_window_unref (event.window);
+}
+
 static void
 gtk_text_view_draw (GtkWidget *widget, GdkRectangle *area)
-{  
+{
+  GdkRectangle intersection;
+  GtkTextView *text_view;
+
+  text_view = GTK_TEXT_VIEW (widget);
+  
   gtk_text_view_paint (widget, area);
 
   /* If the area overlaps the "edge" of the widget, draw the focus
    * rectangle
    */
-  if (TRUE || area->x < FOCUS_EDGE_WIDTH ||
+  if (area->x < FOCUS_EDGE_WIDTH ||
       area->y < FOCUS_EDGE_WIDTH ||
       (area->x + area->width) > (widget->allocation.width - FOCUS_EDGE_WIDTH) ||
       (area->y + area->height) > (widget->allocation.height - FOCUS_EDGE_WIDTH))
     gtk_widget_draw_focus (widget);
+
+  /* Synthesize expose events for the user-drawn border windows,
+   * just as we would for a drawing area.
+   */
+
+  if (text_view->left_window &&
+      gdk_rectangle_intersect (area, &text_view->left_window->allocation,
+                               &intersection))
+    send_expose (text_view, text_view->left_window, &intersection);
+  
+  if (text_view->right_window &&
+      gdk_rectangle_intersect (area, &text_view->right_window->allocation,
+                               &intersection))
+    send_expose (text_view, text_view->right_window, &intersection);
+
+  if (text_view->top_window &&
+      gdk_rectangle_intersect (area, &text_view->top_window->allocation,
+                               &intersection))
+    send_expose (text_view, text_view->top_window, &intersection);
+  
+  if (text_view->bottom_window &&
+      gdk_rectangle_intersect (area, &text_view->bottom_window->allocation,
+                               &intersection))
+    send_expose (text_view, text_view->bottom_window, &intersection);
 }
 
 static gint
@@ -1872,37 +2073,6 @@ gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
   return TRUE;
 }
 
-#include <execinfo.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-static void
-print_backtrace (void)
-{
-  char **symbols;
-  void *addresses[50];
-  int i;
-  int size;
-
-  size = backtrace (addresses, 50);
-  
-  symbols = backtrace_symbols (addresses, size);
-
-  printf ("TRACE\n");
-  
-  i = 0;
-  while (i < size)
-    {
-      printf ("  %s\n", symbols[i]);
-      
-      ++i;
-    }
-
-  free (symbols);
-  
-  printf ("END\n");
-}
-
 static void
 gtk_text_view_draw_focus (GtkWidget *widget)
 {
@@ -3052,6 +3222,27 @@ gtk_text_view_value_changed (GtkAdjustment *adj,
 
   if (dx != 0 || dy != 0)
     {
+      if (dy != 0)
+        {
+          if (text_view->left_window)
+            text_window_scroll (text_view->left_window, 0, dy);
+          if (text_view->right_window)
+            text_window_scroll (text_view->right_window, 0, dy);
+        }
+
+      if (dx != 0)
+        {
+          if (text_view->top_window)
+            text_window_scroll (text_view->top_window, dx, 0);
+          if (text_view->bottom_window)
+            text_window_scroll (text_view->bottom_window, dx, 0);
+        }
+
+      /* It looks nicer to scroll the main area last, because
+       * it takes a while, and making the side areas update
+       * afterward emphasizes the slowness of scrolling the
+       * main area.
+       */
       text_window_scroll (text_view->text_window, dx, dy);
     }
 }
@@ -3244,8 +3435,17 @@ text_window_realize (GtkTextWindow *win,
       
       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
                                         win->window);
-    }
 
+
+      gdk_window_set_background (win->bin_window,
+                                 &win->widget->style->base[GTK_WIDGET_STATE (win->widget)]);
+    }
+  else
+    {
+      gdk_window_set_background (win->bin_window,
+                                 &win->widget->style->bg[GTK_WIDGET_STATE (win->widget)]);
+    }
+  
   g_object_set_qdata (G_OBJECT (win->window),
                       g_quark_from_static_string ("gtk-text-view-text-window"),
                       win);
@@ -3347,10 +3547,31 @@ gtk_text_view_get_window (GtkTextView *text_view,
       break;
       
     case GTK_TEXT_WINDOW_LEFT:
+      if (text_view->left_window)
+        return text_view->left_window->bin_window;
+      else
+        return NULL;
+      break;
+      
     case GTK_TEXT_WINDOW_RIGHT:
+      if (text_view->right_window)
+        return text_view->right_window->bin_window;
+      else
+        return NULL;
+      break;
+      
     case GTK_TEXT_WINDOW_TOP:
+      if (text_view->top_window)
+        return text_view->top_window->bin_window;
+      else
+        return NULL;
+      break;
+      
     case GTK_TEXT_WINDOW_BOTTOM:
-      return NULL;
+      if (text_view->bottom_window)
+        return text_view->bottom_window->bin_window;
+      else
+        return NULL;
       break;
 
     default:
@@ -3385,6 +3606,68 @@ gtk_text_view_get_window_type (GtkTextView *text_view,
     }
 }
 
+static void
+buffer_to_widget (GtkTextView      *text_view,
+                  gint              buffer_x,
+                  gint              buffer_y,
+                  gint             *window_x,
+                  gint             *window_y)
+{
+  if (window_x)
+    {
+      *window_x = buffer_x - text_view->xoffset + FOCUS_EDGE_WIDTH;
+      if (text_view->left_window)
+        *window_x += text_view->left_window->allocation.width;
+    }
+  
+  if (window_y)
+    {
+      *window_y = buffer_y - text_view->yoffset + FOCUS_EDGE_WIDTH;
+      if (text_view->top_window)
+        *window_y += text_view->top_window->allocation.height;
+    }
+}
+
+static void
+widget_to_text_window (GtkTextWindow *win,
+                       gint           widget_x,
+                       gint           widget_y,
+                       gint          *window_x,
+                       gint          *window_y)
+{
+  if (window_x)
+    *window_x = widget_x - win->allocation.x;
+
+  if (window_y)
+    *window_y = widget_y - win->allocation.y;
+}
+
+static void
+buffer_to_text_window (GtkTextView   *text_view,
+                       GtkTextWindow *win,
+                       gint           buffer_x,
+                       gint           buffer_y,
+                       gint          *window_x,
+                       gint          *window_y)
+{
+  if (win == NULL)
+    {
+      g_warning ("Attempt to convert text buffer coordinates to coordinates "
+                 "for a nonexistent child window of GtkTextView");
+      return;
+    }
+  
+  buffer_to_widget (text_view,
+                    buffer_x, buffer_y,
+                    window_x, window_y);
+
+  widget_to_text_window (win,
+                         window_x ? *window_x : 0,
+                         window_y ? *window_y : 0,
+                         window_x,
+                         window_y);
+}
+
 void
 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
                                        GtkTextWindowType win,
@@ -3393,24 +3676,14 @@ gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
                                        gint             *window_x,
                                        gint             *window_y)
 {
-  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
 
   switch (win)
     {
     case GTK_TEXT_WINDOW_WIDGET:
-      if (window_x)
-        {
-          *window_x = buffer_x - text_view->xoffset + FOCUS_EDGE_WIDTH;
-          if (text_view->left_window)
-            *window_x += text_view->left_window->allocation.width;
-        }
-
-      if (window_y)
-        {
-          *window_y = buffer_y - text_view->yoffset + FOCUS_EDGE_WIDTH;
-          if (text_view->top_window)
-            *window_y += text_view->top_window->allocation.height;
-        }
+      buffer_to_widget (text_view,
+                        buffer_x, buffer_y,
+                        window_x, window_y);
       break;
       
     case GTK_TEXT_WINDOW_TEXT:
@@ -3421,10 +3694,31 @@ gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
       break;
       
     case GTK_TEXT_WINDOW_LEFT:
+      buffer_to_text_window (text_view,
+                             text_view->left_window,
+                             buffer_x, buffer_y,
+                             window_x, window_y);
+      break;
+      
     case GTK_TEXT_WINDOW_RIGHT:
+      buffer_to_text_window (text_view,
+                             text_view->right_window,
+                             buffer_x, buffer_y,
+                             window_x, window_y);
+      break;
+      
     case GTK_TEXT_WINDOW_TOP:
+      buffer_to_text_window (text_view,
+                             text_view->top_window,
+                             buffer_x, buffer_y,
+                             window_x, window_y);
+      break;
+      
     case GTK_TEXT_WINDOW_BOTTOM:
-      g_warning ("FIXME");
+      buffer_to_text_window (text_view,
+                             text_view->bottom_window,
+                             buffer_x, buffer_y,
+                             window_x, window_y);
       break;
 
     default:
@@ -3433,6 +3727,70 @@ gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
     }
 }
 
+static void
+widget_to_buffer (GtkTextView *text_view,
+                  gint         widget_x,
+                  gint         widget_y,
+                  gint        *buffer_x,
+                  gint        *buffer_y)
+{
+  if (buffer_x)
+    {
+      *buffer_x = widget_x - FOCUS_EDGE_WIDTH + text_view->xoffset;
+      if (text_view->left_window)
+        *buffer_x -= text_view->left_window->allocation.width;
+    }
+  
+  if (buffer_y)
+    {
+      *buffer_y = widget_y - FOCUS_EDGE_WIDTH + text_view->yoffset;
+      if (text_view->top_window)
+        *buffer_y -= text_view->top_window->allocation.height;
+    }
+}
+
+static void
+text_window_to_widget (GtkTextWindow *win,
+                       gint           window_x,
+                       gint           window_y,
+                       gint          *widget_x,
+                       gint          *widget_y)
+{
+  if (widget_x)
+    *widget_x = window_x + win->allocation.x;
+
+  if (widget_y)
+    *widget_y = window_y + win->allocation.y;
+}
+
+static void
+text_window_to_buffer (GtkTextView   *text_view,
+                       GtkTextWindow *win,
+                       gint           window_x,
+                       gint           window_y,
+                       gint          *buffer_x,
+                       gint          *buffer_y)
+{
+  if (win == NULL)
+    {
+      g_warning ("Attempt to convert GtkTextView buffer coordinates into "
+                 "coordinates for a nonexistent child window.");
+      return;
+    }
+  
+  text_window_to_widget (win,
+                         window_x,
+                         window_y,
+                         buffer_x,
+                         buffer_y);
+
+  widget_to_buffer (text_view,
+                    buffer_x ? *buffer_x : 0,
+                    buffer_y ? *buffer_y : 0,
+                    buffer_x,
+                    buffer_y);
+}
+
 void
 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
                                        GtkTextWindowType win,
@@ -3441,24 +3799,14 @@ gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
                                        gint             *buffer_x,
                                        gint             *buffer_y)
 {
-  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
 
   switch (win)
     {
     case GTK_TEXT_WINDOW_WIDGET:
-      if (buffer_x)
-        {
-          *buffer_x = window_x - FOCUS_EDGE_WIDTH + text_view->xoffset;
-          if (text_view->left_window)
-            *buffer_x -= text_view->left_window->allocation.width;
-        }
-
-      if (buffer_y)
-        {
-          *buffer_y = window_y - FOCUS_EDGE_WIDTH + text_view->yoffset;
-          if (text_view->top_window)
-            *buffer_y -= text_view->top_window->allocation.height;
-        }
+      widget_to_buffer (text_view,
+                        window_x, window_y,
+                        buffer_x, buffer_y);
       break;
       
     case GTK_TEXT_WINDOW_TEXT:
@@ -3469,10 +3817,31 @@ gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
       break;
       
     case GTK_TEXT_WINDOW_LEFT:
+      text_window_to_buffer (text_view,
+                             text_view->left_window,
+                             window_x, window_y,
+                             buffer_x, buffer_y);
+      break;
+      
     case GTK_TEXT_WINDOW_RIGHT:
+      text_window_to_buffer (text_view,
+                             text_view->right_window,
+                             window_x, window_y,
+                             buffer_x, buffer_y);
+      break;
+      
     case GTK_TEXT_WINDOW_TOP:
+      text_window_to_buffer (text_view,
+                             text_view->top_window,
+                             window_x, window_y,
+                             buffer_x, buffer_y);
+      break;
+      
     case GTK_TEXT_WINDOW_BOTTOM:
-      g_warning ("FIXME");
+      text_window_to_buffer (text_view,
+                             text_view->bottom_window,
+                             window_x, window_y,
+                             buffer_x, buffer_y);
       break;
 
     default:
@@ -3481,3 +3850,113 @@ gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
     }
 }
 
+static void
+set_window_width (GtkTextView      *text_view,
+                  gint              width,
+                  GtkTextWindowType type,
+                  GtkTextWindow   **winp)
+{
+  if (width == 0)
+    {
+      if (*winp)
+        {
+          text_window_free (*winp);
+          *winp = NULL;
+          gtk_widget_queue_resize (GTK_WIDGET (text_view));
+        }
+    }
+  else
+    {
+      if (*winp == NULL)
+        {
+          *winp = text_window_new (type,
+                                   GTK_WIDGET (text_view),
+                                   width, 0);
+        }
+      else
+        {
+          if ((*winp)->requisition.width == width)
+            return;
+        }
+
+      gtk_widget_queue_resize (GTK_WIDGET (text_view));
+    }
+}
+
+
+static void
+set_window_height (GtkTextView      *text_view,
+                   gint              height,
+                   GtkTextWindowType type,
+                   GtkTextWindow   **winp)
+{
+  if (height == 0)
+    {
+      if (*winp)
+        {
+          text_window_free (*winp);
+          *winp = NULL;
+          gtk_widget_queue_resize (GTK_WIDGET (text_view));
+        }
+    }
+  else
+    {
+      if (*winp == NULL)
+        {
+          *winp = text_window_new (type,
+                                   GTK_WIDGET (text_view),
+                                   0, height);
+        }
+      else
+        {
+          if ((*winp)->requisition.height == height)
+            return;
+        }
+
+      gtk_widget_queue_resize (GTK_WIDGET (text_view));
+    }
+}
+
+void
+gtk_text_view_set_left_window_width (GtkTextView *text_view,
+                                     gint         width)
+{
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (width >= 0);
+        
+  set_window_width (text_view, width, GTK_TEXT_WINDOW_LEFT,
+                    &text_view->left_window);
+}
+
+void
+gtk_text_view_set_right_window_width (GtkTextView *text_view,
+                                      gint         width)
+{
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (width >= 0);
+
+  set_window_width (text_view, width, GTK_TEXT_WINDOW_RIGHT,
+                    &text_view->right_window);
+}
+
+void
+gtk_text_view_set_top_window_height (GtkTextView *text_view,
+                                     gint         height)
+{
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (height >= 0);
+
+  set_window_height (text_view, height, GTK_TEXT_WINDOW_TOP,
+                     &text_view->top_window);
+}
+
+void
+gtk_text_view_set_bottom_window_height (GtkTextView *text_view,
+                                        gint         height)
+{
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (height >= 0);
+
+  set_window_height (text_view, height, GTK_TEXT_WINDOW_BOTTOM,
+                     &text_view->bottom_window);
+}
index 6ba130a055f1bbf7c81ea4970e4ca1c7d10b3da9..f7f1c357b2929b63d51a15fa95ebfb50e1cedd48 100644 (file)
@@ -1408,6 +1408,154 @@ view_set_title (View *view)
   g_free (title);
 }
 
+static void
+get_lines (GtkTextView  *text_view,
+           gint          first_y,
+           gint          last_y,
+           gint        **buffer_coords,
+           gint        **numbers,
+           gint         *countp)
+{
+  GtkTextIter iter;
+  gint count;
+  gint size;
+  
+  if (buffer_coords)
+    *buffer_coords = NULL;
+
+  if (numbers)
+    *numbers = NULL;
+  
+  /* Get iter at first y */
+  gtk_text_view_get_iter_at_location (text_view, &iter, 0, first_y);
+
+  /* Move back to start of its paragraph */
+  gtk_text_iter_set_line_offset (&iter, 0);
+
+  /* For each iter, get its location and add it to the arrays.
+   * Stop when we pass last_y
+   */
+  count = 0;
+  size = 0;
+
+  while (!gtk_text_iter_is_last (&iter))
+    {
+      GdkRectangle loc;
+
+      gtk_text_view_get_iter_location (text_view, &iter, &loc);
+
+      if (loc.y >= last_y)
+        break;
+
+      if (count >= size)
+        {
+          size = 2 * size + 2; /* + 2 handles size == 0 case */
+          
+          if (buffer_coords)
+            *buffer_coords = g_realloc (*buffer_coords,
+                                        size * sizeof (gint));
+
+          if (numbers)
+            *numbers = g_realloc (*numbers,
+                                  size * sizeof (gint));
+        }
+
+      if (buffer_coords)
+        (*buffer_coords)[count] = loc.y;
+
+      if (numbers)
+        (*numbers)[count] = gtk_text_iter_get_line (&iter);
+      
+      ++count;
+
+      gtk_text_iter_forward_line (&iter);
+    }
+
+  *countp = count;
+}
+
+static gint
+line_numbers_expose (GtkWidget      *widget,
+                     GdkEventExpose *event,
+                     gpointer        user_data)
+{
+  gint count;
+  gint *numbers = NULL;
+  gint *pixels = NULL;
+  gint first_y;
+  gint last_y;
+  gint i;
+  GdkWindow *left_win;
+  PangoLayout *layout;
+  GtkTextView *text_view;
+
+  text_view = GTK_TEXT_VIEW (widget);
+  
+  /* See if this expose is on the line numbers window */
+  left_win = gtk_text_view_get_window (text_view,
+                                       GTK_TEXT_WINDOW_LEFT);
+
+  if (event->window != left_win)
+    return FALSE;
+  
+  first_y = event->area.y;
+  last_y = first_y + event->area.height;
+
+  gtk_text_view_window_to_buffer_coords (text_view,
+                                         GTK_TEXT_WINDOW_LEFT,
+                                         first_y,
+                                         last_y,
+                                         &first_y,
+                                         &last_y);
+
+  get_lines (text_view,
+             first_y,
+             last_y,
+             &pixels,
+             &numbers,
+             &count);
+
+
+  /* Draw fully internationalized numbers! */
+  
+  layout = gtk_widget_create_pango_layout (widget, "");
+  
+  i = 0;
+  while (i < count)
+    {
+      gint pos;
+      gchar *str;
+      
+      gtk_text_view_buffer_to_window_coords (text_view,
+                                             GTK_TEXT_WINDOW_LEFT,
+                                             0,
+                                             pixels[i],
+                                             NULL,
+                                             &pos);
+
+      str = g_strdup_printf ("%d", numbers[i]);
+
+      pango_layout_set_text (layout, str, -1);
+      
+      gdk_draw_layout (left_win,
+                       widget->style->fg_gc [widget->state],
+                       /* 2 is just a random padding */
+                       2, pos + 2,
+                       layout);
+
+      g_free (str);
+      
+      ++i;
+    }
+
+  g_free (pixels);
+  g_free (numbers);
+  
+  g_object_unref (G_OBJECT (layout));
+
+  return TRUE;
+}
+
 static View *
 create_view (Buffer *buffer)
 {
@@ -1449,8 +1597,32 @@ create_view (Buffer *buffer)
                                  GTK_POLICY_AUTOMATIC);
 
   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
-  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
+                               GTK_WRAPMODE_WORD);
+
 
+  /* Set sizes on these windows, just for debugging */
+  
+  gtk_text_view_set_right_window_width (GTK_TEXT_VIEW (view->text_view),
+                                       30);
+  
+  gtk_text_view_set_top_window_height (GTK_TEXT_VIEW (view->text_view),
+                                       15);
+
+  gtk_text_view_set_bottom_window_height (GTK_TEXT_VIEW (view->text_view),
+                                          23);
+
+  /* Draw line numbers in the left window; we should really be
+   * more scientific about what width we set it to.
+   */
+  gtk_text_view_set_left_window_width (GTK_TEXT_VIEW (view->text_view),
+                                       30);
+  
+  gtk_signal_connect (GTK_OBJECT (view->text_view),
+                      "expose_event",
+                      GTK_SIGNAL_FUNC (line_numbers_expose),
+                      NULL);
+  
   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
   gtk_container_add (GTK_CONTAINER (sw), view->text_view);
 
index 6ba130a055f1bbf7c81ea4970e4ca1c7d10b3da9..f7f1c357b2929b63d51a15fa95ebfb50e1cedd48 100644 (file)
@@ -1408,6 +1408,154 @@ view_set_title (View *view)
   g_free (title);
 }
 
+static void
+get_lines (GtkTextView  *text_view,
+           gint          first_y,
+           gint          last_y,
+           gint        **buffer_coords,
+           gint        **numbers,
+           gint         *countp)
+{
+  GtkTextIter iter;
+  gint count;
+  gint size;
+  
+  if (buffer_coords)
+    *buffer_coords = NULL;
+
+  if (numbers)
+    *numbers = NULL;
+  
+  /* Get iter at first y */
+  gtk_text_view_get_iter_at_location (text_view, &iter, 0, first_y);
+
+  /* Move back to start of its paragraph */
+  gtk_text_iter_set_line_offset (&iter, 0);
+
+  /* For each iter, get its location and add it to the arrays.
+   * Stop when we pass last_y
+   */
+  count = 0;
+  size = 0;
+
+  while (!gtk_text_iter_is_last (&iter))
+    {
+      GdkRectangle loc;
+
+      gtk_text_view_get_iter_location (text_view, &iter, &loc);
+
+      if (loc.y >= last_y)
+        break;
+
+      if (count >= size)
+        {
+          size = 2 * size + 2; /* + 2 handles size == 0 case */
+          
+          if (buffer_coords)
+            *buffer_coords = g_realloc (*buffer_coords,
+                                        size * sizeof (gint));
+
+          if (numbers)
+            *numbers = g_realloc (*numbers,
+                                  size * sizeof (gint));
+        }
+
+      if (buffer_coords)
+        (*buffer_coords)[count] = loc.y;
+
+      if (numbers)
+        (*numbers)[count] = gtk_text_iter_get_line (&iter);
+      
+      ++count;
+
+      gtk_text_iter_forward_line (&iter);
+    }
+
+  *countp = count;
+}
+
+static gint
+line_numbers_expose (GtkWidget      *widget,
+                     GdkEventExpose *event,
+                     gpointer        user_data)
+{
+  gint count;
+  gint *numbers = NULL;
+  gint *pixels = NULL;
+  gint first_y;
+  gint last_y;
+  gint i;
+  GdkWindow *left_win;
+  PangoLayout *layout;
+  GtkTextView *text_view;
+
+  text_view = GTK_TEXT_VIEW (widget);
+  
+  /* See if this expose is on the line numbers window */
+  left_win = gtk_text_view_get_window (text_view,
+                                       GTK_TEXT_WINDOW_LEFT);
+
+  if (event->window != left_win)
+    return FALSE;
+  
+  first_y = event->area.y;
+  last_y = first_y + event->area.height;
+
+  gtk_text_view_window_to_buffer_coords (text_view,
+                                         GTK_TEXT_WINDOW_LEFT,
+                                         first_y,
+                                         last_y,
+                                         &first_y,
+                                         &last_y);
+
+  get_lines (text_view,
+             first_y,
+             last_y,
+             &pixels,
+             &numbers,
+             &count);
+
+
+  /* Draw fully internationalized numbers! */
+  
+  layout = gtk_widget_create_pango_layout (widget, "");
+  
+  i = 0;
+  while (i < count)
+    {
+      gint pos;
+      gchar *str;
+      
+      gtk_text_view_buffer_to_window_coords (text_view,
+                                             GTK_TEXT_WINDOW_LEFT,
+                                             0,
+                                             pixels[i],
+                                             NULL,
+                                             &pos);
+
+      str = g_strdup_printf ("%d", numbers[i]);
+
+      pango_layout_set_text (layout, str, -1);
+      
+      gdk_draw_layout (left_win,
+                       widget->style->fg_gc [widget->state],
+                       /* 2 is just a random padding */
+                       2, pos + 2,
+                       layout);
+
+      g_free (str);
+      
+      ++i;
+    }
+
+  g_free (pixels);
+  g_free (numbers);
+  
+  g_object_unref (G_OBJECT (layout));
+
+  return TRUE;
+}
+
 static View *
 create_view (Buffer *buffer)
 {
@@ -1449,8 +1597,32 @@ create_view (Buffer *buffer)
                                  GTK_POLICY_AUTOMATIC);
 
   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
-  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
+                               GTK_WRAPMODE_WORD);
+
 
+  /* Set sizes on these windows, just for debugging */
+  
+  gtk_text_view_set_right_window_width (GTK_TEXT_VIEW (view->text_view),
+                                       30);
+  
+  gtk_text_view_set_top_window_height (GTK_TEXT_VIEW (view->text_view),
+                                       15);
+
+  gtk_text_view_set_bottom_window_height (GTK_TEXT_VIEW (view->text_view),
+                                          23);
+
+  /* Draw line numbers in the left window; we should really be
+   * more scientific about what width we set it to.
+   */
+  gtk_text_view_set_left_window_width (GTK_TEXT_VIEW (view->text_view),
+                                       30);
+  
+  gtk_signal_connect (GTK_OBJECT (view->text_view),
+                      "expose_event",
+                      GTK_SIGNAL_FUNC (line_numbers_expose),
+                      NULL);
+  
   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
   gtk_container_add (GTK_CONTAINER (sw), view->text_view);